#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import threading
import subprocess
import socket
import ssl
from urllib.parse import urlparse, parse_qs
from pathlib import Path
import time

class DashboardHandler(BaseHTTPRequestHandler):
    def __init__(self, proxy_instance, *args, **kwargs):
        self.proxy = proxy_instance
        super().__init__(*args, **kwargs)

    def do_GET(self):
        parsed_path = urlparse(self.path)
        path = parsed_path.path
        query_params = parse_qs(parsed_path.query)
        
        if path == '/':
            self.serve_dashboard()
        elif path == '/api/requests':
            self.serve_requests_api()
        elif path == '/api/summary':
            self.serve_summary_api()
        elif path == '/api/analysis':
            self.serve_analysis_api()
        elif path.startswith('/static/'):
            self.serve_static_file(path)
        else:
            self.send_error(404)
    
    def do_POST(self):
        parsed_path = urlparse(self.path)
        path = parsed_path.path
        
        content_length = int(self.headers.get('Content-Length', 0))
        post_data = self.rfile.read(content_length).decode('utf-8')
        
        if path == '/api/replay':
            self.handle_replay_request(post_data)
        else:
            self.send_error(404)
    
    def handle_replay_request(self, post_data):
        try:
            request_data = json.loads(post_data)
            
            # 解析重放请求
            method_line = request_data.get('method_line', '')
            headers = request_data.get('headers', {})
            body = request_data.get('body', '')
            
            # 构建完整HTTP请求
            http_request = method_line + '\r\n'
            for key, value in headers.items():
                http_request += f"{key}: {value}\r\n"
            http_request += '\r\n'
            if body:
                http_request += body
            
            # 解析目标地址
            parts = method_line.split(' ')
            if len(parts) >= 2:
                url = parts[1]
                parsed_url = urlparse(url)
                
                if parsed_url.scheme == 'https' or parsed_url.port == 443:
                    response = self.replay_https_request(parsed_url.netloc, http_request)
                else:
                    response = self.replay_http_request(parsed_url.netloc, http_request)
                
                # 返回响应
                self.send_response(200)
                self.send_header('Content-type', 'application/json')
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                
                result = {
                    'success': True,
                    'response': response[:2000],  # 限制响应长度
                    'message': '请求重放成功'
                }
                self.wfile.write(json.dumps(result, ensure_ascii=False).encode('utf-8'))
            else:
                raise Exception('无效的请求格式')
                
        except Exception as e:
            self.send_response(500)
            self.send_header('Content-type', 'application/json')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            
            result = {
                'success': False,
                'error': str(e),
                'message': '请求重放失败'
            }
            self.wfile.write(json.dumps(result, ensure_ascii=False).encode('utf-8'))
    
    def replay_http_request(self, host_port, request):
        try:
            if ':' in host_port:
                host, port = host_port.split(':')
                port = int(port)
            else:
                host = host_port
                port = 80
            
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(10)
            sock.connect((host, port))
            sock.send(request.encode('utf-8'))
            
            response = ''
            while True:
                try:
                    data = sock.recv(4096)
                    if not data:
                        break
                    response += data.decode('utf-8', errors='ignore')
                except socket.timeout:
                    break
            
            sock.close()
            return response
            
        except Exception as e:
            return f'HTTP请求错误: {str(e)}'
    
    def replay_https_request(self, host_port, request):
        try:
            if ':' in host_port:
                host, port = host_port.split(':')
                port = int(port)
            else:
                host = host_port
                port = 443
            
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(10)
            
            # 包装为SSL连接
            context = ssl.create_default_context()
            context.check_hostname = False
            context.verify_mode = ssl.CERT_NONE
            
            ssl_sock = context.wrap_socket(sock, server_hostname=host)
            ssl_sock.connect((host, port))
            ssl_sock.send(request.encode('utf-8'))
            
            response = ''
            while True:
                try:
                    data = ssl_sock.recv(4096)
                    if not data:
                        break
                    response += data.decode('utf-8', errors='ignore')
                except socket.timeout:
                    break
            
            ssl_sock.close()
            return response
            
        except Exception as e:
            return f'HTTPS请求错误: {str(e)}'

    def serve_dashboard(self):
        html_content = self.get_dashboard_html()
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write(html_content.encode())

    def serve_requests_api(self):
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()
        
        requests_data = {
            'requests': self.proxy.requests_log[-100:],  # Last 100 requests
            'timestamp': time.time(),
            'total_count': len(self.proxy.requests_log)
        }
        
        self.wfile.write(json.dumps(requests_data, indent=2).encode())

    def serve_summary_api(self):
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()
        
        summary = self.proxy.get_requests_summary()
        self.wfile.write(json.dumps(summary, indent=2).encode())

    def serve_analysis_api(self):
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()
        
        try:
            from request_analyzer import IDEARequestAnalyzer
            analyzer = IDEARequestAnalyzer()
            report = analyzer.generate_traffic_report(self.proxy.requests_log)
            self.wfile.write(json.dumps(report, indent=2).encode())
        except ImportError:
            error_response = {'error': 'Request analyzer not available'}
            self.wfile.write(json.dumps(error_response).encode())

    def get_dashboard_html(self):
        return """<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IDEA Proxy Monitor</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f5f5f5;
        }
        
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            border-radius: 10px;
            margin-bottom: 20px;
        }
        
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 20px;
            margin-bottom: 20px;
        }
        
        .stat-card {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        
        .stat-number {
            font-size: 2em;
            font-weight: bold;
            color: #667eea;
        }
        
        .requests-table {
            background: white;
            border-radius: 8px;
            overflow: hidden;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
        }
        
        th, td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #eee;
        }
        
        th {
            background-color: #f8f9fa;
            font-weight: 600;
        }
        
        .method-get { color: #28a745; }
        .method-post { color: #007bff; }
        .method-put { color: #ffc107; }
        .method-delete { color: #dc3545; }
        
        .idea-request {
            background-color: #fff3cd;
        }
        
        .refresh-btn {
            background: #667eea;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            margin-bottom: 20px;
        }
        
        .refresh-btn:hover {
            background: #5a67d8;
        }
        
        .controls {
            margin-bottom: 20px;
            display: flex;
            gap: 10px;
            align-items: center;
        }
        
        .action-btn {
            background: #28a745;
            color: white;
            border: none;
            padding: 5px 10px;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
            margin-right: 5px;
        }
        
        .action-btn:hover {
            opacity: 0.8;
        }
        
        .action-btn.copy {
            background: #17a2b8;
        }
        
        .action-btn.replay {
            background: #ffc107;
            color: #212529;
        }
        
        .modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.4);
        }
        
        .modal-content {
            background-color: #fefefe;
            margin: 5% auto;
            padding: 20px;
            border: none;
            border-radius: 8px;
            width: 80%;
            max-width: 800px;
            max-height: 80%;
            overflow-y: auto;
        }
        
        .close {
            color: #aaa;
            float: right;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
        }
        
        .close:hover {
            color: black;
        }
        
        .request-details {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 5px;
            margin: 10px 0;
        }
        
        .request-details pre {
            background: white;
            padding: 10px;
            border-radius: 3px;
            overflow-x: auto;
            max-height: 300px;
        }
        
        .replay-result {
            background: #d4edda;
            border: 1px solid #c3e6cb;
            border-radius: 5px;
            padding: 15px;
            margin: 10px 0;
        }
        
        .replay-error {
            background: #f8d7da;
            border: 1px solid #f5c6cb;
            border-radius: 5px;
            padding: 15px;
            margin: 10px 0;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>IDEA Proxy Monitor</h1>
        <p>Real-time monitoring of IntelliJ IDEA network requests</p>
    </div>
    
    <div class="controls">
        <button class="refresh-btn" onclick="refreshData()">Refresh</button>
        <label>
            <input type="checkbox" id="autoRefresh" checked>
            Auto-refresh (5s)
        </label>
        <label>
            <input type="checkbox" id="showIdeaOnly">
            Show IDEA requests only
        </label>
    </div>
    
    <div class="stats-grid">
        <div class="stat-card">
            <div class="stat-number" id="totalRequests">0</div>
            <div>Total Requests</div>
        </div>
        <div class="stat-card">
            <div class="stat-number" id="ideaRequests">0</div>
            <div>IDEA Requests</div>
        </div>
        <div class="stat-card">
            <div class="stat-number" id="recentRequests">0</div>
            <div>Last 5 Minutes</div>
        </div>
        <div class="stat-card">
            <div class="stat-number" id="securityIssues">0</div>
            <div>Security Concerns</div>
        </div>
    </div>
    
    <div class="requests-table">
        <table>
            <thead>
                <tr>
                    <th>Time</th>
                    <th>Method</th>
                    <th>URL</th>
                    <th>Client</th>
                    <th>Type</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody id="requestsTableBody">
                <tr><td colspan="6">Loading...</td></tr>
            </tbody>
        </table>
    </div>
    
    <!-- 请求详情模态框 -->
    <div id="requestModal" class="modal">
        <div class="modal-content">
            <span class="close" onclick="closeModal()">&times;</span>
            <div id="requestDetailsContent">
                <!-- 内容将通过JavaScript动态填充 -->
            </div>
        </div>
    </div>

    <script>
        let autoRefreshInterval;
        
        function refreshData() {
            fetch('/api/requests')
                .then(response => response.json())
                .then(data => {
                    updateStats(data);
                    updateRequestsTable(data.requests);
                })
                .catch(error => console.error('Error:', error));
                
            fetch('/api/analysis')
                .then(response => response.json())
                .then(data => {
                    updateAnalysisStats(data);
                })
                .catch(error => console.error('Analysis error:', error));
        }
        
        function updateStats(data) {
            document.getElementById('totalRequests').textContent = data.total_count || 0;
            
            // Count recent requests (last 5 minutes)
            const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
            const recentCount = data.requests.filter(req => 
                new Date(req.timestamp) > fiveMinutesAgo
            ).length;
            document.getElementById('recentRequests').textContent = recentCount;
        }
        
        function updateAnalysisStats(analysisData) {
            if (analysisData.summary) {
                document.getElementById('ideaRequests').textContent = 
                    analysisData.summary.idea_related_requests || 0;
            }
            
            if (analysisData.security_summary) {
                document.getElementById('securityIssues').textContent = 
                    analysisData.security_summary.total_concerns || 0;
            }
        }
        
        function updateRequestsTable(requests) {
            const tbody = document.getElementById('requestsTableBody');
            const showIdeaOnly = document.getElementById('showIdeaOnly').checked;
            
            if (!requests || requests.length === 0) {
                tbody.innerHTML = '<tr><td colspan="6">No requests captured yet</td></tr>';
                return;
            }
            
            let filteredRequests = requests;
            if (showIdeaOnly) {
                filteredRequests = requests.filter(req => 
                    req.headers && req.headers['User-Agent'] && 
                    req.headers['User-Agent'].toLowerCase().includes('idea')
                );
            }
            
            const reversedRequests = filteredRequests.slice(-50).reverse();
            const rows = reversedRequests.map((req, index) => {
                const time = new Date(req.timestamp).toLocaleTimeString();
                const method = req.method_line ? req.method_line.split(' ')[0] : 'Unknown';
                const url = req.method_line ? req.method_line.split(' ')[1] : 'Unknown';
                const client = req.client_addr || 'Unknown';
                const userAgent = req.headers && req.headers['User-Agent'] ? 
                    req.headers['User-Agent'].substring(0, 50) + '...' : 'Unknown';
                
                const isIdeaRequest = userAgent.toLowerCase().includes('idea') || 
                                    userAgent.toLowerCase().includes('jetbrains');
                
                return `
                    <tr class="${isIdeaRequest ? 'idea-request' : ''}">
                        <td>${time}</td>
                        <td><span class="method-${method.toLowerCase()}">${method}</span></td>
                        <td title="${url}">${url.length > 60 ? url.substring(0, 60) + '...' : url}</td>
                        <td>${client}</td>
                        <td title="${userAgent}">${userAgent}</td>
                        <td>
                            <button class="action-btn copy" onclick="copyRequest(${index})">复制</button>
                            <button class="action-btn replay" onclick="replayRequest(${index})">重放</button>
                            <button class="action-btn" onclick="showRequestDetails(${index})">详情</button>
                        </td>
                    </tr>
                `;
            }).join('');
            
            tbody.innerHTML = rows || '<tr><td colspan="6">No matching requests</td></tr>';
            
            // 保存当前请求数据供操作使用
            window.currentRequests = reversedRequests;
        }
        
        function toggleAutoRefresh() {
            const checkbox = document.getElementById('autoRefresh');
            if (checkbox.checked) {
                autoRefreshInterval = setInterval(refreshData, 5000);
            } else {
                clearInterval(autoRefreshInterval);
            }
        }
        
        // 请求操作函数
        function copyRequest(index) {
            const req = window.currentRequests[index];
            if (!req) return;
            
            const requestText = `${req.method_line}\n` + 
                Object.entries(req.headers || {}).map(([k,v]) => `${k}: ${v}`).join('\n') + 
                '\n\n' + (req.body || '');
            
            navigator.clipboard.writeText(requestText).then(() => {
                alert('请求已复制到剪贴板');
            }).catch(err => {
                // 备用方案
                const textArea = document.createElement('textarea');
                textArea.value = requestText;
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
                alert('请求已复制到剪贴板');
            });
        }
        
        function replayRequest(index) {
            const req = window.currentRequests[index];
            if (!req) return;
            
            if (!confirm('确定要重放这个请求吗？')) return;
            
            const replayData = {
                method_line: req.method_line,
                headers: req.headers || {},
                body: req.body || ''
            };
            
            fetch('/api/replay', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(replayData)
            })
            .then(response => response.json())
            .then(data => {
                showReplayResult(data);
            })
            .catch(error => {
                alert('重放请求时出错: ' + error.message);
            });
        }
        
        function showRequestDetails(index) {
            const req = window.currentRequests[index];
            if (!req) return;
            
            const modal = document.getElementById('requestModal');
            const content = document.getElementById('requestDetailsContent');
            
            const headersText = Object.entries(req.headers || {}).map(([k,v]) => `${k}: ${v}`).join('\n');
            
            content.innerHTML = `
                <h3>请求详情</h3>
                <div class="request-details">
                    <h4>请求行</h4>
                    <pre>${req.method_line || 'N/A'}</pre>
                    
                    <h4>请求头</h4>
                    <pre>${headersText || 'No headers'}</pre>
                    
                    <h4>客户端地址</h4>
                    <pre>${req.client_addr || 'N/A'}</pre>
                    
                    <h4>时间戳</h4>
                    <pre>${req.timestamp || 'N/A'}</pre>
                    
                    <h4>原始请求 (前500字符)</h4>
                    <pre>${req.raw_request || 'N/A'}</pre>
                </div>
            `;
            
            modal.style.display = 'block';
        }
        
        function showReplayResult(result) {
            const modal = document.getElementById('requestModal');
            const content = document.getElementById('requestDetailsContent');
            
            const resultClass = result.success ? 'replay-result' : 'replay-error';
            const resultText = result.success ? result.response : result.error;
            
            content.innerHTML = `
                <h3>重放结果</h3>
                <div class="${resultClass}">
                    <h4>${result.message}</h4>
                    <pre>${resultText}</pre>
                </div>
            `;
            
            modal.style.display = 'block';
        }
        
        // 模态框关闭功能
        function closeModal() {
            document.getElementById('requestModal').style.display = 'none';
        }
        
        // 点击模态框外部关闭
        window.onclick = function(event) {
            const modal = document.getElementById('requestModal');
            if (event.target === modal) {
                modal.style.display = 'none';
            }
        }
        
        // Initialize
        document.getElementById('autoRefresh').addEventListener('change', toggleAutoRefresh);
        document.getElementById('showIdeaOnly').addEventListener('change', refreshData);
        
        refreshData();
        toggleAutoRefresh();
    </script>
</body>
</html>"""

    def log_message(self, format, *args):
        # Suppress HTTP server logs
        pass


class ProxyDashboard:
    def __init__(self, proxy_instance, port=8081):
        self.proxy = proxy_instance
        self.port = port
        self.server = None
        self.thread = None

    def start(self):
        """Start the dashboard server"""
        def handler(*args, **kwargs):
            return DashboardHandler(self.proxy, *args, **kwargs)
        
        self.server = HTTPServer(('0.0.0.0', self.port), handler)
        self.thread = threading.Thread(target=self.server.serve_forever)
        self.thread.daemon = True
        self.thread.start()
        
        # 获取WSL IP地址
        try:
            result = subprocess.run(['hostname', '-I'], capture_output=True, text=True, timeout=5)
            wsl_ip = result.stdout.strip().split()[0] if result.stdout.strip() else "localhost"
            print(f"🌐 Dashboard started at:")
            print(f"   WSL访问: http://localhost:{self.port}")
            print(f"   Windows访问: http://{wsl_ip}:{self.port}")
        except:
            print(f"🌐 Dashboard started at http://0.0.0.0:{self.port}")
            print("   获取IP失败，请手动使用WSL IP地址访问")

    def stop(self):
        """Stop the dashboard server"""
        if self.server:
            self.server.shutdown()
            self.server.server_close()
        if self.thread:
            self.thread.join()


if __name__ == '__main__':
    # For testing purposes
    class MockProxy:
        def __init__(self):
            self.requests_log = [
                {
                    'timestamp': '2024-01-15T10:30:00',
                    'method_line': 'GET https://plugins.jetbrains.com/api/plugins HTTP/1.1',
                    'client_addr': '127.0.0.1:12345',
                    'headers': {'user-agent': 'IntelliJ IDEA/2023.3'}
                },
                {
                    'timestamp': '2024-01-15T10:31:00', 
                    'method_line': 'POST https://www.jetbrains.com/api/license HTTP/1.1',
                    'client_addr': '127.0.0.1:12346',
                    'headers': {'user-agent': 'IntelliJ IDEA/2023.3'}
                }
            ]
        
        def get_requests_summary(self):
            return {
                'total_requests': len(self.requests_log),
                'recent_requests': self.requests_log[-5:]
            }
    
    mock_proxy = MockProxy()
    dashboard = ProxyDashboard(mock_proxy)
    dashboard.start()
    
    try:
        print("Dashboard running... Press Ctrl+C to stop")
        import time
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        dashboard.stop()
        print("Dashboard stopped")